﻿using OpenCvSharp.Internal;
using OpenCvSharp.Internal.Vectors;

namespace OpenCvSharp;

static partial class Cv2
{
    /// <summary>
    /// Groups the object candidate rectangles.
    /// </summary>
    /// <param name="rectList"> Input/output vector of rectangles. Output vector includes retained and grouped rectangles.</param>
    /// <param name="groupThreshold">Minimum possible number of rectangles minus 1. The threshold is used in a group of rectangles to retain it.</param>
    /// <param name="eps"></param>
    public static void GroupRectangles(IList<Rect> rectList, int groupThreshold, double eps = 0.2)
    {
        if (rectList is null)
            throw new ArgumentNullException(nameof(rectList));

        using var rectListVec = new VectorOfRect(rectList);

        NativeMethods.HandleException(
            NativeMethods.objdetect_groupRectangles1(rectListVec.CvPtr, groupThreshold, eps));

        ClearAndAddRange(rectList, rectListVec.ToArray());
    }

    /// <summary>
    /// Groups the object candidate rectangles.
    /// </summary>
    /// <param name="rectList"> Input/output vector of rectangles. Output vector includes retained and grouped rectangles.</param>
    /// <param name="weights"></param>
    /// <param name="groupThreshold">Minimum possible number of rectangles minus 1. The threshold is used in a group of rectangles to retain it.</param>
    /// <param name="eps">Relative difference between sides of the rectangles to merge them into a group.</param>
    public static void GroupRectangles(IList<Rect> rectList, out int[] weights, int groupThreshold, double eps = 0.2)
    {
        if (rectList is null)
            throw new ArgumentNullException(nameof(rectList));

        using var rectListVec = new VectorOfRect(rectList);
        using var weightsVec = new VectorOfInt32();

        NativeMethods.HandleException(
            NativeMethods.objdetect_groupRectangles2(rectListVec.CvPtr, weightsVec.CvPtr, groupThreshold, eps));

        ClearAndAddRange(rectList, rectListVec.ToArray());
        weights = weightsVec.ToArray();
    }

    /// <summary>
    /// Groups the object candidate rectangles.
    /// </summary>
    /// <param name="rectList"></param>
    /// <param name="groupThreshold"></param>
    /// <param name="eps"></param>
    /// <param name="weights"></param>
    /// <param name="levelWeights"></param>
    public static void GroupRectangles(IList<Rect> rectList, int groupThreshold, double eps, out int[] weights, out double[] levelWeights)
    {
        if (rectList is null)
            throw new ArgumentNullException(nameof(rectList));

        using var rectListVec = new VectorOfRect(rectList);
        using var weightsVec = new VectorOfInt32();
        using var levelWeightsVec = new VectorOfDouble();

        NativeMethods.HandleException(
            NativeMethods.objdetect_groupRectangles3(
                rectListVec.CvPtr, groupThreshold, eps, weightsVec.CvPtr, levelWeightsVec.CvPtr));

        ClearAndAddRange(rectList, rectListVec.ToArray());
        weights = weightsVec.ToArray();
        levelWeights = levelWeightsVec.ToArray();
    }

    /// <summary>
    /// Groups the object candidate rectangles.
    /// </summary>
    /// <param name="rectList"></param>
    /// <param name="rejectLevels"></param>
    /// <param name="levelWeights"></param>
    /// <param name="groupThreshold"></param>
    /// <param name="eps"></param>
    public static void GroupRectangles(IList<Rect> rectList, out int[] rejectLevels, out double[] levelWeights, int groupThreshold, double eps = 0.2)
    {
        if (rectList is null)
            throw new ArgumentNullException(nameof(rectList));

        using var rectListVec = new VectorOfRect(rectList);
        using var rejectLevelsVec = new VectorOfInt32();
        using var levelWeightsVec = new VectorOfDouble();

        NativeMethods.HandleException(
            NativeMethods.objdetect_groupRectangles4(
                rectListVec.CvPtr, rejectLevelsVec.CvPtr, levelWeightsVec.CvPtr, groupThreshold, eps));

        ClearAndAddRange(rectList, rectListVec.ToArray());
        rejectLevels = rejectLevelsVec.ToArray();
        levelWeights = levelWeightsVec.ToArray();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="rectList"></param>
    /// <param name="foundWeights"></param>
    /// <param name="foundScales"></param>
    /// <param name="detectThreshold"></param>
    /// <param name="winDetSize"></param>
    public static void GroupRectanglesMeanshift(IList<Rect> rectList, out double[] foundWeights,
        out double[] foundScales, double detectThreshold = 0.0, Size? winDetSize = null)
    {
        if (rectList is null)
            throw new ArgumentNullException(nameof(rectList));

        var winDetSize0 = winDetSize.GetValueOrDefault(new Size(64, 128));

        using var rectListVec = new VectorOfRect(rectList);
        using var foundWeightsVec = new VectorOfDouble();
        using var foundScalesVec = new VectorOfDouble();

        NativeMethods.HandleException(
            NativeMethods.objdetect_groupRectangles_meanshift(
                rectListVec.CvPtr, foundWeightsVec.CvPtr, foundScalesVec.CvPtr, detectThreshold, winDetSize0));

        ClearAndAddRange(rectList, rectListVec.ToArray());
        foundWeights = foundWeightsVec.ToArray();
        foundScales = foundScalesVec.ToArray();
    }

    private static void ClearAndAddRange<T>(ICollection<T> list, IEnumerable<T> values)
    {
        list.Clear();
        foreach (var t in values)
        {
            list.Add(t);
        }
    }
}
